//构造函数
//拷贝函数
//析构函数
//赋值运算符重载函数
#ifdef a
#include<iostream>
using namespace std;
namespace N1 {
	//类中可以进行定义和初始化
	int g = 3 + 5;
	int a = 2;
	
	//无法进行赋值操作
	//a =	6;
	//a + -2;
	
	int b = a + g;
	double c;
	//嵌套定义
	namespace N2 {
		int a;
		double b;
		int c[10];
	}
}

namespace N1 {
	//int a;	//重定义
	int e[5];
}

//三种namespace引用方式(:: -》 域操作符)
//first:	using namespace N1;
//second:	N1::a
//third:	using N1::a

#endif 



#ifdef a
//缺省参数->缺省默认值不可在定义和声明中重复给出，通常写在声明中，且默认值必须为常量或全局变量；
#include<iostream>
using namespace std;
//全缺省参数
//半缺省参数-》只能从右到左依次给出
//根据函数调用约定（_cdecl/thiscall）,函数传递实参，从右向左开始压栈，
int main() {
	//调用约定的测试，从右往左
	int a = 0;
	cout << a++ << a++ << a++ << a++ << a++ << endl;
	// 4 3 2 1 0;
	return 0;
}
#endif	



#ifdef a
//函数重载
//什么是函数重载：一词多义，同名函数参数列表不同（参数个数，参数类型），但不包括返回值类型。
//注意：函数的参数列表不受缺省参数的影响。
//vs下c++函数重载原理：函数名的命名规范-》 (? + 函数名 + @ + @ + YA + 函数参数类型 + @z)-》s:int   N：double
//linux下：_z + 函数名字符数 + 函数名 + 形参类型表示字母
#endif

#ifdef a
//内联函数-》c语言宏函数的替换
//以空间换时间，inline只是一个内联建议，是否采取要看编译器。
//类中成员函数默认具有内联建议。
//inline放在声明前不起任何作用
#endif 


#ifdef a
//auto 根据初始化表达式的类型来替换auto
#include<iostream>
using namespace std;
int main() {
	int a = 3.14;
	cout << typeid(a).name() << endl;
	//基于范围的for循环
	int arr[5] = { 1,2,3,4,5 };
	for (int i = 0; i < 5; i++)
		cout << arr[i] << ' ';
	cout << endl;
	//auto
	for (auto e : arr){
		e *= 2;
		cout << e << ' ';
	}
	cout << endl;
	for (auto e : arr)
		cout << e << ' ';
	//auto&
	cout << endl;
	for (auto& e : arr)
		e *= 2;
	for (auto e : arr)
		cout << e << ' ';

	return  0;
}
#endif 




#ifdef a
//构造函数
//无函数类型，无返回值，函数名同类名
#include<iostream>
using namespace std;
class Date {
private:
	int _year;
	int _month;
	int _day;
public:
	//构造函数
//	Date(int month, int day, int year = 2022) {
//		_year = year;
//		_month = month;
//		_day = day;
//		return;
//	}
	
	Date(int month = 6, int day = 1, int year = 2022) {
		_year = year;
		_month = month;
		_day = day;
		return;
	}
	void SetDate(int month, int day, int year = 2022) {
		_year = year;
		_month = month;
		_day = day;
		return;
	}
	
	void MyPrintDate() {
		cout << _year << ':' << _month << ':' << _day << endl;
		return;
	}
};
int main(){
	Date d1;
	//Date d2();//声明
	d1.MyPrintDate();
	return 0;
}
#endif 


#ifdef a
//默认构造函数赋值是随机值？
//构造函数
//无函数类型，无返回值，函数名同类名
#include<iostream>
using namespace std;
class Date {													  
private:
	int _year;
	int _month;
	int _day;
public:
	//不写构造函数，构造函数为无参默认函数，测试默认值是什么
	void SetDate(int month, int day, int year = 2022) {
		_year = year;
		_month = month;
		_day = day;
		return;
	}

	void MyPrintDate() {
		cout << _year << ':' << _month << ':' << _day << endl;
		return;
	}
};
int main() {
	Date d1;
	d1.MyPrintDate();
	//-858993460:-858993460:-858993460

	return 0;
}
#endif

//默认构造函数只能有一个（全缺省构造函数/无参构造函数）
//对于自定义类型，编译器生成的默认构造函数会调用自定义类型的默认构造函数’
//构造函数作为一个特殊的成员函数，其作用并不是为了创建对象（对象大小的空间早就已经预留好了），调用构造函数只是为了对对象进行初始化。


#ifdef a
//默认构造函数会自己创造空间吗?->目前看来应该不会（2022 6 1）
#include<iostream>
using namespace std;
class Test {
private:
	int* arr;
};
int main() {
	Test t;
	cout << _CrtDumpMemoryLeaks();//检查内存泄漏
	return 0;
}
#endif 



#ifdef a
//析构函数
//同构造函数相反，析构函数是为了完成类相关资源的清理工作（而不是为了完成对象的销毁，局部对象的销毁工作是由编译器进行的，即析构函数负责空间释放，不负责栈帧的销毁）
//析构函数无返回值，且没有参数，函数名为 ~+类名
//一个类有且只能有一个析构函数，不支持重载

//当类中不涉及资源管理时，可以不用写析构函数，但一旦类中设计到了资源管理，就一定要写出析构函数（默认析构函数不可行），虽然也可以
//使用free释放资源，但需要手动进行，而析构函数编译器会在对象生命周期结束时自动调用，更方便快捷。


//简易栈（压栈、出栈、top、size）
#include<iostream>
#include<assert.h>
using namespace std;

//重命名
typedef int TypeData;

class Stack {
private:
	TypeData* _array;
	size_t _capacity;//容量
	size_t _size;//有效元素
public:
	//构造函数
	Stack() {
		_array = (TypeData*)malloc(sizeof(TypeData) * 10);
		if (!_array) 
			assert(0);
		_capacity = 10;
		_size = 0;
		return;
	}
//	析构函数
	~Stack() {
		free(_array);
		_array = NULL;
		_size = 0;
		_capacity = 0;
	}
	//压栈
	void Push(const TypeData& data) {
		//容量检查
		_array[_size++] = data;
	}
	//出栈
	void Pop() {
		if (Empty())
			return;
		_size--;
	}
	//查空
	bool Empty() {
		return _size == 0;
	}
	//返回栈顶
	size_t Top() {
		return _array[_size - 1];
	}
	//返回有效元素数
	size_t Size() {
		return _size;
	}
	//打印栈的元素
	void Print() {
		for (size_t i = 0; i < _size; i++)
			cout << _array[i] << ' ';
		cout << endl;
	}
};


void TestStack() {
	Stack s;
	s.Push(1);
	s.Push(2);
	s.Push(3);
	s.Push(4);
	s.Print();
	cout << "size = " << s.Size() << endl;
	cout << "top =" << s.Top() << endl;

	s.Top();
	s.Top();
	s.Print();
	
	cout <<"size = " << s.Size() << endl;
	cout << "top =" <<  s.Top() << endl;

}

//默认析构函数不会释放内存空间
int main() {
	
	TestStack();
	/*int* arr = (int*)malloc(sizeof(int) * 5);
	if (!arr)
		assert(0);
	*/


	//发生内存泄漏，返回true,未发生返回false
	cout << _CrtDumpMemoryLeaks();//检测内存泄漏
	return 0;
}
#endif 




#ifdef a
//拷贝构造函数
//拷贝构造函数是构造函数的一种重载形式，无返回值，函数名为类名，参数唯一，为类对象的引用，使用const进行修饰，由编译器自动调用
//无显性定义时，默认生成

#include<iostream>
using namespace std;
class Date{
private:
	int _year;
	int _month;
	int _day;
public:
	Date(int month, int day, int year = 2022) {
		_year = year;
		_month = month;
		_day = day;
	}
	~Date() {
		_year = 0;
		_month = 0;
		_day = 0;
	}
	//参数必须为引用传递，若为值传递，因为要发生临时拷贝，就会发生无限嵌套；
	Date(const Date& d) {
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void PrintDate() {
		cout << _year << ':' << _month << ':' << _day << endl;
	}
};

void TestDate() {
	Date d1(6,1);
	Date d2(d1);
	d1.PrintDate();
	d2.PrintDate();
}
int main() {
	TestDate();
	return 0;
}
//默认的拷贝构造函数按照内存存储以字节序完成拷贝，即完成原封不动的拷贝，两个对象中的指针指向同一地址，
//编译器在自行调用析构函数时，会使用两个析构函数来释放两个对象的资源，释放完第一个对象中的指针后，第二个对象中的指针变为野指针，
//再次释放，视为对野指针的访问，代码崩溃，这种拷贝我们称之为浅拷贝。

//因为浅拷贝的原因，一旦涉及到资源管理，拷贝构造函数必须自行定义
#endif 


#ifdef a
//采用默认拷贝构造函数对涉及到资源管理的对象进行拷贝测试
#include<iostream>
using namespace std;
class Date {
private:
	int _year;
	int _month;
	int _day;
	int* _arr;
public:
	Date(int month, int day, int year = 2022) {
		_year = year;
		_month = month;
		_day = day;
		_arr = (int*)malloc(sizeof(int) * 10);
	}
	~Date() {
		_year = 0;
		_month = 0;
		_day = 0;
		free(_arr);
		_arr = nullptr;
	}
	
	/*Date(const Date& d) {
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}*/
	void PrintDate() {
		cout << _year << ':' << _month << ':' << _day << endl;
	}
};

void TestDate() {
	Date d1(6, 1);
	Date d2(d1);
	d1.PrintDate();
	d2.PrintDate();
}
int main() {
	TestDate();
	return 0;
}
//会发生崩溃问题
#endif 



#ifdef a
//this指针的形式 *const

//赋值运算符的重载->operator操作符
//Date d1(d2) 与 Date d1 = d2; 完全相同，都调用了拷贝构造函数，属于浅拷贝
//但当d1,d2都存在时，d1 = d2 ，调用的是赋值运算符重载函数，属于浅拷贝
//赋值运算符的重载只能写到类中，不能作为全局函数，因为类会有默认的赋值运算符重载函数，写到类外会重定义。

#endif 

#ifdef a
//const成员函数
//this指针形式*const,而const成员函数就是对this指针的修饰，即形式转为const * const(常量指针常量），其指向内容和指针本身均不可更改
//只有非静态成员函数才可以使用const
//const成员函数内部不能调用非const成员函数
//非const成员函数内部可以调用const成员函数
//const对象不可调用非const成员函数
//非const对象可以调用const成员函数

//析构函数/构造函数/拷贝构造函数/赋值运算符重载函数都不可以使用const修饰
//关键字mutable修饰的成员变量永远处于可更改状态，即在const成员函数中用mutable修饰的成员变量是可以更改的。
#endif 

#ifdef a
//取地址重载/const取地址重载->默认成员函数，无显性定义时默认给出。
#include<iostream>
using namespace std;
class Date {
private:
	int _year;
	int _month;
	int _day;
public:
	Date(int day = 3, int month = 6, int year = 2020) {
		_day = day;
		_month = month;
		_year = year;
	}
	//取地址重载
	Date* opertor&() {
		return this;
	}
	//const取地址重载
	const Date* operator&()const {
		return this;
	}

};
int main() {
	return 0;
}
#endif




#ifdef a
//构造函数
//构造函数内部是赋值，而不是初始化，初始化-》初始化列表

//每个成员在初始化列表中只能出现一次，即初始化只能进行一次，赋值却可以进行多次
#include<iostream>
using namespace std;
class Time {
private:
	int _time;
public:
	Time(int time)
		:_time(time)
	{
		_time = time;
	}
	void Print() {
		cout << _time << endl;
	}
};

class Date {
public:
	int _year;
	int _month = 6;//c++11 中允许内部给定缺省值
	//C++11支持非静态成员变量在声明时进行初始化赋值，但是要注意这里不是初始化，这里是给声明的成员量缺省值。
	mutable int _day;
	const int _x; //= 2;
	int& _y;// = _month;
	Time _z;//
	//当Time类有默认构造函数（全缺省或无参构造函数）时，创建Date类对象时，会首先开始执行Date类构造函数的初始化列表，
			//然后自动在初始化列表中调用Time类的默认构造函数进行初始化，之后真正进入到Date类的构造函数中，也就是说，不管是否在Date
			// 类构造函数中对_z进行定义，编译器都会自动在初始化列表中首先进行一次初始化，因此使用初始化列表更高效。
			
			// 但当Time类无默认构造函数时，编译器就无法在初始化列表中对该类成员进行初始化。

	//类是对成员变量的声明，所谓类的初始化列表：就是对成员变量进行初始化，开辟空间的地方，对于普通的局部变量来说
	//，即便不初始化，也可以直接进行赋值，于是在定义时并要求强制初始化。但有些变量必须在定义的时候进行初始化，如const变量，该变量无法在
	//初始化之后进行赋值，因此必须在初始化列表中进行初始化或根据c++ 11 特性给予缺省值（此处不是,这里是给声明的成员变量缺省值）;
	//引用类变量必须初始化或给予缺省值，因为引用对象无法更改
	//自定义类型变量必须进行初始化，因为c++有着面向对象的特性，只有进行初始化后才能说有了具体的对象。
public:
	Date(int day = 2, int month = 6, int year = 2020, int x = 0, int y = 0)
		:_year(year)
		, _month(month)
		, _day(day)
		, _x(x)//必须初始化
		, _y(y)
		, _z(1)//
	{
		//_x = x;-->错误说明内部是赋值，而不是初始化（const只能进行初始化，不可赋值）
		//_y = y;-->引用必须初始化，之后才能进行赋值
	}
};


void TestDate() {
	Date d1;
	return;
}
int main() {
	TestDate();
	return 0;
}
#endif 











